#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
OpenBMC ABI语义分析工具

功能：
1. 分析函数的有状态/无状态特性
2. 分类接口类型（同步/异步、DBus/IPMI/PLDM等）
3. 判断线程安全性
4. 评估复杂度和风险级别

使用方法：
    python3 abi_analyzer.py --input libsdbusplus.so.1.0.0.abi.json --output analyzed.xlsx
"""

import json
import sys
from pathlib import Path
from typing import Dict, List, Optional
from dataclasses import dataclass, asdict
import re

try:
    import pandas as pd
    import openpyxl
    from openpyxl.styles import Font, PatternFill, Alignment
    HAS_EXCEL = True
except ImportError:
    HAS_EXCEL = False


# 状态性评分权重配置
SCORE_IS_MEMBER_FUNCTION = 3          # 成员函数（通过this操作）
SCORE_NON_CONST_MEMBER = 2             # 非const成员函数（可能修改状态）
SCORE_RETURNS_POINTER = 2              # 返回指针/引用（可能返回内部状态）
SCORE_HAS_CONTEXT_PARAM = 1            # 有上下文参数
SCORE_CREATE_DESTROY_PATTERN = 3       # create/new/delete/destroy模式
SCORE_GET_SET_PATTERN = 1              # get/set模式
SCORE_CALLBACK_PARAM = 2               # 有回调参数（可能有状态）

# 状态性判断阈值
STATEFUL_THRESHOLD = 4                 # 分数>=4判定为有状态


@dataclass
class FunctionAnalysis:
    """函数分析结果"""
    # 基本信息
    library_name: str
    function_name: str
    demangled_name: str
    namespace: str
    class_name: str
    
    # 状态性分析
    statefulness: str           # "stateful" / "stateless" / "unknown"
    statefulness_score: int     # 状态性评分（0-10）
    statefulness_reasons: List[str]  # 判断依据
    
    # 接口分类
    protocol_category: str      # "dbus" / "ipmi" / "pldm" / "mctp" / "generic"
    domain_category: str        # "communication" / "logging" / "sensor" / "power" 等
    call_pattern: str           # "synchronous" / "asynchronous" / "callback" / "unknown"
    
    # 其他特性
    is_constructor: bool
    is_destructor: bool
    is_const: bool
    is_template: bool
    
    # 复杂度评估
    complexity: str             # "low" / "medium" / "high"
    risk_level: str             # "low" / "medium" / "high"
    
    # 线程安全性（初步判断）
    thread_safety: str          # "safe" / "unsafe" / "unknown"


class ABIAnalyzer:
    """ABI语义分析器"""
    
    def __init__(self, abi_data_path: str):
        """
        初始化分析器
        
        参数:
            abi_data_path: ABI数据JSON文件路径
        """
        self.abi_data_path = Path(abi_data_path)
        if not self.abi_data_path.exists():
            raise FileNotFoundError(f"ABI数据文件不存在: {abi_data_path}")
        
        # 加载数据
        with open(self.abi_data_path, 'r', encoding='utf-8') as f:
            self.abi_data = json.load(f)
        
        self.library_name = self.abi_data['library']['name']
        self.analyses = []
    
    def analyze(self) -> List[FunctionAnalysis]:
        """
        执行完整的语义分析
        
        返回:
            函数分析结果列表
        """
        print(f"[INFO] 开始分析库: {self.library_name}")
        
        # 只分析函数类型的符号
        function_symbols = [
            s for s in self.abi_data['symbols'] 
            if s['symbol_type'] == 'function'
        ]
        
        print(f"[INFO] 共 {len(function_symbols)} 个函数需要分析")
        
        for idx, symbol in enumerate(function_symbols, 1):
            if idx % 100 == 0:
                print(f"[INFO] 进度: {idx}/{len(function_symbols)}")
            
            analysis = self._analyze_function(symbol)
            self.analyses.append(analysis)
        
        print(f"[SUCCESS] 分析完成！共 {len(self.analyses)} 个函数")
        return self.analyses
    
    def _analyze_function(self, symbol: Dict) -> FunctionAnalysis:
        """分析单个函数"""
        demangled = symbol.get('demangled_name', symbol['name'])
        
        # 1. 状态性分析
        statefulness_score, reasons = self._analyze_statefulness(symbol, demangled)
        statefulness = self._determine_statefulness(statefulness_score)
        
        # 2. 协议分类
        protocol = self._classify_protocol(symbol, demangled)
        
        # 3. 功能域分类
        domain = self._classify_domain(symbol, demangled)
        
        # 4. 调用模式
        call_pattern = self._classify_call_pattern(symbol, demangled)
        
        # 5. 复杂度评估
        complexity = self._assess_complexity(symbol, demangled)
        
        # 6. 风险评估
        risk = self._assess_risk(symbol, statefulness, complexity)
        
        # 7. 线程安全性（初步判断）
        thread_safety = self._assess_thread_safety(symbol, statefulness)
        
        return FunctionAnalysis(
            library_name=self.library_name,
            function_name=symbol.get('function_name', ''),
            demangled_name=demangled,
            namespace=symbol.get('namespace', ''),
            class_name=symbol.get('class_name', ''),
            statefulness=statefulness,
            statefulness_score=statefulness_score,
            statefulness_reasons=reasons,
            protocol_category=protocol,
            domain_category=domain,
            call_pattern=call_pattern,
            is_constructor=symbol.get('is_constructor', False),
            is_destructor=symbol.get('is_destructor', False),
            is_const=symbol.get('is_const', False),
            is_template=symbol.get('is_template', False),
            complexity=complexity,
            risk_level=risk,
            thread_safety=thread_safety
        )
    
    def _analyze_statefulness(self, symbol: Dict, demangled: str) -> tuple[int, List[str]]:
        """
        分析函数的有状态/无状态特性
        
        返回:
            (评分, 判断依据列表)
        """
        score = 0
        reasons = []
        
        # 规则1: 是否为成员函数
        if symbol.get('class_name'):
            score += SCORE_IS_MEMBER_FUNCTION
            reasons.append(f"成员函数（类: {symbol['class_name']}）")
        
        # 规则2: 是否为非const成员函数
        if symbol.get('class_name') and not symbol.get('is_const', False):
            score += SCORE_NON_CONST_MEMBER
            reasons.append("非const成员函数，可能修改对象状态")
        
        # 规则3: 函数名模式分析
        func_name = (symbol.get('function_name') or '').lower()
        
        # create/new/destroy/delete模式
        if any(pattern in func_name for pattern in ['create', 'new', 'delete', 'destroy', 'init', 'close']):
            score += SCORE_CREATE_DESTROY_PATTERN
            reasons.append(f"资源管理函数（{func_name}）")
        
        # get/set模式
        if func_name.startswith('get_') or func_name.startswith('set_'):
            score += SCORE_GET_SET_PATTERN
            reasons.append(f"属性访问器（{func_name}）")
        
        # 规则4: 返回值分析（通过符号名推断）
        if '*' in demangled or '&' in demangled:
            # 返回指针或引用，可能返回内部状态
            if not symbol.get('is_const', False):
                score += SCORE_RETURNS_POINTER
                reasons.append("返回非const指针/引用")
        
        # 规则5: 参数模式分析
        # 检查是否有callback、handler、context等参数
        if any(keyword in demangled.lower() for keyword in ['callback', 'handler', 'context', 'userdata']):
            score += SCORE_CALLBACK_PARAM
            reasons.append("包含回调或上下文参数")
        
        # 规则6: 构造函数/析构函数
        if symbol.get('is_constructor'):
            score += SCORE_CREATE_DESTROY_PATTERN
            reasons.append("构造函数（创建对象状态）")
        elif symbol.get('is_destructor'):
            score += SCORE_CREATE_DESTROY_PATTERN
            reasons.append("析构函数（销毁对象状态）")
        
        if not reasons:
            reasons.append("无明显状态特征，初步判定为无状态")
        
        return score, reasons
    
    def _determine_statefulness(self, score: int) -> str:
        """根据评分确定状态性"""
        if score >= STATEFUL_THRESHOLD:
            return "stateful"
        elif score > 0:
            return "likely_stateless"  # 有一些状态特征，但不明显
        else:
            return "stateless"
    
    def _classify_protocol(self, symbol: Dict, demangled: str) -> str:
        """分类协议类型"""
        namespace = (symbol.get('namespace') or '').lower()
        demangled_lower = demangled.lower() if demangled else ''
        
        # 基于命名空间判断
        if 'sdbusplus' in namespace or 'dbus' in namespace:
            return "dbus"
        elif 'pldm' in namespace:
            return "pldm"
        elif 'mctp' in namespace:
            return "mctp"
        elif 'ipmi' in namespace or 'ipmi' in demangled_lower:
            return "ipmi"
        
        # 基于函数名判断
        if 'dbus' in demangled_lower or 'bus' in demangled_lower:
            return "dbus"
        elif 'pldm' in demangled_lower:
            return "pldm"
        elif 'mctp' in demangled_lower:
            return "mctp"
        elif 'ipmi' in demangled_lower:
            return "ipmi"
        
        return "generic"
    
    def _classify_domain(self, symbol: Dict, demangled: str) -> str:
        """分类功能域"""
        demangled_lower = demangled.lower() if demangled else ''
        func_name = (symbol.get('function_name') or '').lower()
        
        # 关键词匹配
        domain_keywords = {
            'communication': ['bus', 'dbus', 'message', 'signal', 'method', 'property', 'interface'],
            'logging': ['log', 'journal', 'trace', 'debug', 'error', 'warning', 'info'],
            'sensor': ['sensor', 'reading', 'threshold', 'monitor', 'hwmon'],
            'power': ['power', 'chassis', 'reset', 'shutdown', 'voltage', 'current'],
            'network': ['network', 'ip', 'mac', 'ethernet', 'addr'],
            'storage': ['storage', 'flash', 'file', 'fru', 'eeprom'],
            'user': ['user', 'auth', 'credential', 'password', 'pam'],
            'inventory': ['inventory', 'fru', 'component', 'entity'],
            'firmware': ['firmware', 'update', 'flash', 'boot', 'image'],
            'gpio': ['gpio', 'pin', 'led'],
            'fan': ['fan', 'cooling', 'pwm'],
            'temperature': ['temp', 'thermal', 'degree'],
        }
        
        for domain, keywords in domain_keywords.items():
            if any(kw in demangled_lower or kw in func_name for kw in keywords):
                return domain
        
        return "generic"
    
    def _classify_call_pattern(self, symbol: Dict, demangled: str) -> str:
        """分类调用模式"""
        demangled_lower = demangled.lower() if demangled else ''
        
        # 异步模式特征
        async_keywords = ['async', 'callback', 'handler', 'promise', 'future', 'then', 'await']
        if any(kw in demangled_lower for kw in async_keywords):
            return "asynchronous"
        
        # 回调模式
        if 'callback' in demangled_lower or 'handler' in demangled_lower:
            return "callback"
        
        # 事件驱动
        func_name_lower = (symbol.get('function_name') or '').lower()
        if 'event' in demangled_lower or 'on_' in func_name_lower:
            return "event_driven"
        
        # 默认同步
        return "synchronous"
    
    def _assess_complexity(self, symbol: Dict, demangled: str) -> str:
        """评估函数复杂度"""
        complexity_score = 0
        
        if not demangled:
            return "low"
        
        # 模板增加复杂度
        if symbol.get('is_template', False):
            complexity_score += 2
        
        # 参数数量（通过括号内容粗略估算）
        paren_start = demangled.find('(')
        paren_end = demangled.rfind(')')
        if paren_start != -1 and paren_end != -1:
            params = demangled[paren_start+1:paren_end]
            # 统计逗号数量估算参数个数
            param_count = params.count(',') + 1 if params.strip() and params != 'void' else 0
            if param_count > 5:
                complexity_score += 2
            elif param_count > 3:
                complexity_score += 1
        
        # 命名空间深度
        namespace = symbol.get('namespace') or ''
        if namespace:
            depth = namespace.count('::') + 1
            if depth > 3:
                complexity_score += 1
        
        # 函数名长度（通常复杂函数名字长）
        func_name_len = len(symbol.get('function_name') or '')
        if func_name_len > 30:
            complexity_score += 1
        
        # 判断
        if complexity_score >= 4:
            return "high"
        elif complexity_score >= 2:
            return "medium"
        else:
            return "low"
    
    def _assess_risk(self, symbol: Dict, statefulness: str, complexity: str) -> str:
        """评估兼容风险级别"""
        risk_score = 0
        
        # 有状态函数风险高
        if statefulness == "stateful":
            risk_score += 2
        
        # 复杂度高风险高
        if complexity == "high":
            risk_score += 2
        elif complexity == "medium":
            risk_score += 1
        
        # 析构函数风险中等（需要正确释放资源）
        if symbol.get('is_destructor', False):
            risk_score += 1
        
        # 模板函数风险中等（可能有实例化问题）
        if symbol.get('is_template', False):
            risk_score += 1
        
        if risk_score >= 4:
            return "high"
        elif risk_score >= 2:
            return "medium"
        else:
            return "low"
    
    def _assess_thread_safety(self, symbol: Dict, statefulness: str) -> str:
        """初步评估线程安全性"""
        # 无状态函数通常线程安全
        if statefulness == "stateless":
            return "likely_safe"
        
        # const成员函数通常线程安全
        if symbol.get('is_const', False):
            return "likely_safe"
        
        # 有状态函数需要进一步分析
        if statefulness == "stateful":
            return "needs_review"
        
        return "unknown"
    
    def save_to_excel(self, output_path: str):
        """保存分析结果到Excel"""
        if not HAS_EXCEL:
            print("[ERROR] 需要安装pandas和openpyxl")
            return
        
        if not self.analyses:
            print("[WARNING] 没有函数可分析，跳过Excel生成")
            return
        
        output_file = Path(output_path)
        output_file.parent.mkdir(parents=True, exist_ok=True)
        
        # 转换为DataFrame
        df = pd.DataFrame([asdict(a) for a in self.analyses])
        
        with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
            # Sheet 1: 完整分析结果
            df.to_excel(writer, sheet_name='完整分析', index=False)
            
            # Sheet 2: 有状态函数
            stateful_df = df[df['statefulness'] == 'stateful']
            stateful_df.to_excel(writer, sheet_name='有状态函数', index=False)
            
            # Sheet 3: 无状态函数
            stateless_df = df[df['statefulness'].isin(['stateless', 'likely_stateless'])]
            stateless_df.to_excel(writer, sheet_name='无状态函数', index=False)
            
            # Sheet 4: 按协议分类
            for protocol in df['protocol_category'].unique():
                protocol_df = df[df['protocol_category'] == protocol]
                sheet_name = f'协议_{protocol}'[:31]  # Excel sheet名称限制31字符
                protocol_df.to_excel(writer, sheet_name=sheet_name, index=False)
            
            # Sheet 5: 统计摘要
            summary_data = {
                '总函数数': [len(df)],
                '有状态': [len(df[df['statefulness'] == 'stateful'])],
                '无状态': [len(df[df['statefulness'].isin(['stateless', 'likely_stateless'])])],
                '未知': [len(df[df['statefulness'] == 'unknown'])],
                '同步调用': [len(df[df['call_pattern'] == 'synchronous'])],
                '异步调用': [len(df[df['call_pattern'] == 'asynchronous'])],
                '高复杂度': [len(df[df['complexity'] == 'high'])],
                '高风险': [len(df[df['risk_level'] == 'high'])],
            }
            summary_df = pd.DataFrame(summary_data).T
            summary_df.columns = ['数量']
            summary_df.to_excel(writer, sheet_name='统计摘要')
        
        # 美化Excel
        self._format_excel(output_file)
        
        print(f"[SUCCESS] 分析结果已保存: {output_path}")
        
        # 同时保存JSON
        json_path = output_file.with_suffix('.analyzed.json')
        self.save_to_json(str(json_path))
    
    def save_to_json(self, output_path: str):
        """保存分析结果到JSON"""
        data = {
            'library': self.library_name,
            'analyzed_at': pd.Timestamp.now().isoformat() if HAS_EXCEL else '',
            'analyses': [asdict(a) for a in self.analyses]
        }
        
        with open(output_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
        
        print(f"[SUCCESS] JSON数据已保存: {output_path}")
    
    def _format_excel(self, excel_path: Path):
        """美化Excel表格"""
        wb = openpyxl.load_workbook(excel_path)
        
        header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
        header_font = Font(bold=True, color="FFFFFF")
        
        for sheet in wb.worksheets:
            # 设置表头样式
            if sheet.max_row > 0:
                for cell in sheet[1]:
                    cell.fill = header_fill
                    cell.font = header_font
                    cell.alignment = Alignment(horizontal='center', vertical='center')
            
            # 自动调整列宽
            for column in sheet.columns:
                max_length = 0
                column_letter = column[0].column_letter
                for cell in column:
                    try:
                        if cell.value:
                            max_length = max(max_length, len(str(cell.value)))
                    except:
                        pass
                adjusted_width = min(max_length + 2, 80)
                sheet.column_dimensions[column_letter].width = adjusted_width
        
        wb.save(excel_path)
    
    def print_summary(self):
        """打印分析摘要"""
        if not self.analyses:
            print("[WARNING] 没有分析结果")
            return
        
        total = len(self.analyses)
        stateful = len([a for a in self.analyses if a.statefulness == 'stateful'])
        stateless = len([a for a in self.analyses if a.statefulness in ['stateless', 'likely_stateless']])
        
        print("\n" + "=" * 80)
        print("分析摘要:")
        print(f"  库: {self.library_name}")
        print(f"  总函数数: {total}")
        print(f"  有状态函数: {stateful} ({stateful/total*100:.1f}%)")
        print(f"  无状态函数: {stateless} ({stateless/total*100:.1f}%)")
        
        # 按协议分类统计
        protocols = {}
        for a in self.analyses:
            protocols[a.protocol_category] = protocols.get(a.protocol_category, 0) + 1
        
        print(f"\n  协议分类:")
        for protocol, count in sorted(protocols.items(), key=lambda x: x[1], reverse=True):
            print(f"    {protocol}: {count}")
        
        print("=" * 80)


def main():
    """主函数"""
    import argparse
    
    parser = argparse.ArgumentParser(description='OpenBMC ABI语义分析工具')
    parser.add_argument('--input', required=True, help='ABI数据JSON文件路径')
    parser.add_argument('--output', required=True, help='输出Excel文件路径')
    parser.add_argument('--verbose', action='store_true', help='显示详细信息')
    
    args = parser.parse_args()
    
    try:
        # 创建分析器
        analyzer = ABIAnalyzer(args.input)
        
        # 执行分析
        analyzer.analyze()
        
        # 保存结果
        if args.output.endswith('.xlsx'):
            analyzer.save_to_excel(args.output)
        else:
            analyzer.save_to_json(args.output)
        
        # 打印摘要
        analyzer.print_summary()
        
        return 0
    
    except Exception as e:
        print(f"[ERROR] 分析失败: {e}", file=sys.stderr)
        if args.verbose:
            import traceback
            traceback.print_exc()
        return 1


if __name__ == '__main__':
    sys.exit(main())

